#pragma once

#include "../umlmodel_global.h"

#include <memory>
#include <vector>

#include "Element.h"
#include "VisibilityKind.h"

using std::make_shared;
using std::shared_ptr;
using std::vector;

namespace uml {
class Dependency;
class Namespace;
class Package;
class StringExpression;

/// <summary>
/// NamedElement
/// A NamedElement is an Element in a model that may have a name. The name may be
/// given directly and/or via the use of a StringExpression.
/// </summary>
class UMLMODEL_EXPORT NamedElement : public Element {

public: // Constructors and destructors
    explicit NamedElement(const QString& umlType="uml::NamedElement");

public: // JsonSerializable override
    void fromJson(const QJsonObject& json, QStringList& errorList) override;
    void toJson(QJsonObject& json) override;

public: // Methods
    /// <summary>
    /// allNamespaces
    /// The query allNamespaces() gives the sequence of Namespaces in which the
    /// NamedElement is nested, working outwards.
    /// </summary>
    virtual shared_ptr<vector<Namespace>> allNamespaces() const;

    /// <summary>
    /// allOwningPackages
    /// The query allOwningPackages() returns the set of all the enclosing Namespaces of
    /// this NamedElement, working outwards, that are Packages, up to but not including
    /// the first such Namespace that is not a Package.
    /// </summary>
    virtual shared_ptr<vector<Package>> allOwningPackages() const;

    /// <summary>
    /// isDistinguishableFrom
    /// The query isDistinguishableFrom() determines whether two NamedElements may
    /// logically co-exist within a Namespace. By default, two named elements are
    /// distinguishable if (a) they have types neither of which is a kind of the other
    /// or (b) they have different names.
    /// </summary>
    virtual bool isDistinguishableFrom(shared_ptr<NamedElement> n, shared_ptr<Namespace> ns) const;

    /// <summary>
    /// separator
    /// The query separator() gives the string that is used to separate names when
    /// constructing a qualifiedName.
    /// </summary>
    virtual QString separator() const;

public: // Properties
    /// <summary>
    /// clientDependencies getter
    /// Indicates the Dependencies that reference this NamedElement as a client.
    /// </summary>
    vector<shared_ptr<Dependency>>& clientDependencies();

    /// <summary>
    /// name getter
    /// The name of the NamedElement.
    /// </summary>
    QString name() const;

    /// <summary>
    /// name setter
    /// The name of the NamedElement.
    /// </summary>
    void setName(const QString& value);

    /// <summary>
    /// nameExpression getter
    /// The StringExpression used to define the name of this NamedElement.
    /// </summary>
    shared_ptr<StringExpression> nameExpression() const;

    /// <summary>
    /// nameExpression setter
    /// The StringExpression used to define the name of this NamedElement.
    /// </summary>
    void setNameExpression(shared_ptr<StringExpression> value);

    /// <summary>
    /// namespace2 getter
    /// Specifies the Namespace that owns the NamedElement.
    /// </summary>
    shared_ptr<Namespace> namespace2() const;

    /// <summary>
    /// namespace2 setter
    /// Specifies the Namespace that owns the NamedElement.
    /// </summary>
    void setNamespace2(shared_ptr<Namespace> value);

    /// <summary>
    /// qualifiedName getter
    /// A name that allows the NamedElement to be identified within a hierarchy of
    /// nested Namespaces. It is constructed from the names of the containing Namespaces
    /// starting at the root of the hierarchy and ending with the name of the
    /// NamedElement itself.
    /// </summary>
    QString qualifiedName() const;

    /// <summary>
    /// qualifiedName setter
    /// A name that allows the NamedElement to be identified within a hierarchy of
    /// nested Namespaces. It is constructed from the names of the containing Namespaces
    /// starting at the root of the hierarchy and ending with the name of the
    /// NamedElement itself.
    /// </summary>
    void setQualifiedName(const QString& value);

    /// <summary>
    /// visibility getter
    /// Determines whether and how the NamedElement is visible outside its owning
    /// Namespace.
    /// </summary>
    VisibilityKind visibility() const;

    /// <summary>
    /// visibility setter
    /// Determines whether and how the NamedElement is visible outside its owning
    /// Namespace.
    /// </summary>
    void setVisibility(VisibilityKind value);

private: // Fields
    /// <summary>
    /// clientDependencies
    /// Indicates the Dependencies that reference this NamedElement as a client.
    /// </summary>
    vector<shared_ptr<Dependency>> clientDependencies_;

    /// <summary>
    /// name
    /// The name of the NamedElement.
    /// </summary>
    const static QString name_default_;
    QString name_= name_default_;

    /// <summary>
    /// nameExpression
    /// The StringExpression used to define the name of this NamedElement.
    /// </summary>
    shared_ptr<StringExpression> nameExpression_;

    /// <summary>
    /// namespace2
    /// Specifies the Namespace that owns the NamedElement.
    /// </summary>
    shared_ptr<Namespace> namespace2_;

    /// <summary>
    /// qualifiedName
    /// A name that allows the NamedElement to be identified within a hierarchy of
    /// nested Namespaces. It is constructed from the names of the containing Namespaces
    /// starting at the root of the hierarchy and ending with the name of the
    /// NamedElement itself.
    /// </summary>
    const static QString qualifiedName_default_;
    QString qualifiedName_= qualifiedName_default_;

    /// <summary>
    /// visibility
    /// Determines whether and how the NamedElement is visible outside its owning
    /// Namespace.
    /// </summary>
    VisibilityKind visibility_ = VisibilityKind::kpublic;

};

} // namespace uml
